----------- 
-- Author: Bojun Chen
-- Date: 2021-09-08 10:53:42
-- Desc: UI管理器
----------- 


local UIManager = {}

local UIConfig = LuaConfig.UIConfig
-- 层级
local LayerDepth = {}
-- 资源加载方式(1 同步)(2 异步)
local ResLoadType = 1

-- 同步加载资源接口
local SyncLoadAsset
-- 异步加载资源接口
local AsyncLoadAsset

-- 所有已被打开窗口
local AllOpenWindows = {}
-- 所有已被关闭的窗口
local AllHideWindows = {}
-- 所有已被打开的界面名字，有顺序
local AllOpenWindowsName = {}

function UIManager.SetLoadFunc(path)
    return ResourceManager.Instance:LoadAssetForLua(path, false)
end

function UIManager.Test()
    UIManager.SetLoadAssetType(1)
    UIManager.SetSyncLoadAssetApi(UIManager.SetLoadFunc)
end



--[[
    @desc: 设置界面的显示深度
    author:Bojun Chen
    time:2021-09-16 16:42:55
    --@view: 界面对象
    @return:
]]
local function SetWinDepth(view)
    -- todo: 针对NGUI的设置，以后使用UGUI的时候还需要重写一个
    local idx = #AllOpenWindowsName
    local preWin = nil
    while idx > 0 do
        local win =  AllOpenWindows[AllOpenWindowsName[idx]]
        if win.config.LAYER == view.config.LAYER then
            preWin = win
            break
        end
        idx = idx - 1
    end    

    local depth = UIConfig.Layer[view.config.LAYER]
    if preWin ~= nil then
        local preWinPanels = preWin.gameObject:GetComponentsInChildren(typeof(UIPanel), true)
        for i = 0, preWinPanels.Length - 1 do
            local dp = preWinPanels[i].depth
            if depth < preWinPanels[i].depth then
                depth = preWinPanels[i].depth
            end
        end
        depth = depth + 10
    end

    local panels = view.gameObject:GetComponentsInChildren(typeof(UIPanel), true)
    for i = 0, panels.Length - 1 do
        panels[i].depth = panels[i].depth + depth
    end
end

--[[
    @desc: 检测已关闭界面是否超过缓存时间，超过的执行销毁
    author:Bojun Chen
    time:2021-09-16 15:49:52
    @return:
]]
local function CheckCache()
    local destroyName = {}
    for k, v in pairs(AllHideWindows) do
        if v:CheckCacheTime() then
            v:Destroy()
            table.insert(destroyName, k)
        end
    end

    for k, v in ipairs(destroyName) do
        AllHideWindows[v] = nil
    end
end

--[[
    @desc: 通过缓存打开界面
    author:Bojun Chen
    time:2021-09-16 17:44:42
    --@win: 界面
	--@callback: 打开后的回调
	--@args: 参数
    @return:
]]
local function OpenByCache(win, callback, ...)
    SetWinDepth(win)
    win:Enable(...)
    if callback ~= nil then callback(win) end
    AllOpenWindows[win.name] = win
    AllHideWindows[win.name] = nil
    table.insert(AllOpenWindowsName, win.name)
end

--[[
    @desc: 通过配置设置界面
    author:Bojun Chen
    time:2021-09-16 17:45:08
    --@config: 界面配置
	--@obj: 对象
	--@callback: 打开后的回调
	--@args: 参数
    @return:
]]
local function SetViewByConfig(config, obj, callback, args)
    if ObjectIsNull(obj) then
        LogError('Load ' .. config.WIN_PREFAB .. 'ui obj faild!')
        return
    end

    local uiRoot = GameFacade.UIRootObj.transform
    local uiObj = InstantiateObj(obj, uiRoot)
    
    -- 2.查找到对应的脚本View与ModelView并且生成脚本对象
    local luaView = require(config.SCRIPT .. 'View')
    local luaViewModel = require(config.SCRIPT .. 'ViewModel')
    local viewModel = luaViewModel.new()
    local view = luaView.new(config, uiObj, viewModel)
    SetWinDepth(view)
    view:Init()
    view:Enable(table.unpack(args))

    if callback ~= nil then callback(view) end
    -- 3.添加到缓存
    AllOpenWindows[view.name] = view
    table.insert(AllOpenWindowsName, view.name)
end


--[[
    @desc: 同步加载界面并打开界面
    author:Bojun Chen
    time:2021-09-16 11:23:48
    --@winName: 界面名字
	--@args: 参数
    @return:
]]
local function OpenSyncWindow(winName, callback, ...)
    if winName == nil or winName == '' then
        LogError('传入的窗口名字为空，请检查！')
        return
    end

    -- 0.查询缓存内是否有该界面
    local config = WindowConfig[winName]
    local win = AllHideWindows[winName]
    if win ~= nil then
        OpenByCache(win, callback, ...)
        return
    end

    -- 1.创建窗口游戏对象，设置窗口层级
    local args = {...}
    local obj = SyncLoadAsset(config.WIN_PREFAB)
    SetViewByConfig(config, obj, callback, args)
end

--[[
    @desc: 异步加载界面资源，并打开界面
    author:Bojun Chen
    time:2021-09-16 17:04:05
    --@winName: 界面名称
	--@callback: 打开成功后的回调
	--@args: 参数
    @return:
]]
local function OpenASyncWindow(winName, callback, ...)
    if winName == nil or winName == '' then
        LogError('传入的窗口名字为空，请检查！')
        return
    end

    -- 0.查询缓存内是否有该界面
    local config = WindowConfig[winName]
    local win = AllHideWindows[winName]
    if win ~= nil then
        OpenByCache(win, callback, ...)
        return
    end

    local args = {...}
    -- 1.创建窗口游戏对象，设置窗口层级
    local obj = AsyncLoadAsset(config.WIN_PREFAB, function(obj)
        SetViewByConfig(config, obj, callback, args)
    end)
end


function UIManager.Init()
    -- 配置层级
    LayerDepth['Base'] = {}
    LayerDepth['Pop'] = {}
    LayerDepth['Guide'] = {}

    -- 同步加载
    ResLoadType = 1
end

--[[
    @desc: 设置加载资源的方式
    author:Bojun Chen
    time:2021-09-16 14:59:39
    --@loadType: [1 同步] [2 异步]
    @return:
]]
function UIManager.SetLoadAssetType(loadType)
    ResLoadType = loadType
end

--[[
    @desc: 设置同步加载接口
    author:Bojun Chen
    time:2021-09-16 14:58:10
    --@syncLoadAssetApi: 加载资源接口
    @return:
]]
function UIManager.SetSyncLoadAssetApi(syncLoadAssetApi)
    SyncLoadAsset = syncLoadAssetApi
end

--[[
    @desc: 设置异步加载接口
    author:Bojun Chen
    time:2021-09-16 14:58:56
    --@asyncLoadAssetApi: 异步加载资源接口
    @return:
]]
function UIManager.SetAsyncLoadAssetApi(asyncLoadAssetApi)
    AsyncLoadAsset = asyncLoadAssetApi
end


--[[
    @desc: 打开界面
    author:Bojun Chen
    time:2021-09-16 15:01:45
    --@winName: 界面名字
	--@callback: 打开界面后的回调(注: 同步异步都用这个)
	--@args: 参数
    @return:
]]
function UIManager.OpenWindow(winName, callback, ...)
    -- 检测缓存的界面是否已经被卸载
    CheckCache()

    if ResLoadType == 1 then
        OpenSyncWindow(winName, callback, ...)
    elseif ResLoadType == 2 then
        OpenASyncWindow(winName, callback, ...)
    end
end


--[[
    @desc: 关闭显示的界面
    author:Bojun Chen
    time:2021-09-16 16:07:58
    --@winName: 界面的名字
	--@callback: 关闭界面时的回调
	--@args: 参数
    @return:
]]
function UIManager.HideWindow(winName, callback, ...)
    local win = AllOpenWindows[winName]
    if win == nil then
        LogError('winname = ' .. winName .. ' window is nil!')
        if callback ~= nil then callback() end
        CheckCache()
        return
    end

    win:Disable(...)
    if callback ~= nil then callback() end

    -- 从显示数组中移除
    for i = #AllOpenWindowsName, 1, -1 do
        if AllOpenWindowsName[i] == winName then
            table.remove(AllOpenWindowsName, i)
            break
        end
    end

    AllHideWindows[winName] = win
    AllOpenWindows[winName] = nil
    CheckCache()
end


--[[
    @desc: 清理所有页面
    author:Bojun Chen
    time:2021-09-16 14:10:28
    @return:
]]
function UIManager.CleanAll()
    for k, v in pairs(AllHideWindows) do
        v:Destroy()
    end

    for k, v in pairs(AllOpenWindows) do
        v:Destroy()
    end

    AllHideWindows = {}
    AllOpenWindows = {}
end



return  UIManager